// Connect.cpp : Implementation of CConnect
#include "stdafx.h"
#include "AddIn.h"
#include "Connect.h"
#include <string>

extern CAddInModule _AtlModule;

// CConnect
STDMETHODIMP CConnect::OnConnection(IDispatch *pApplication, ext_ConnectMode ConnectMode, IDispatch *pAddInInst, SAFEARRAY ** /*custom*/ )
{
	HRESULT hr = S_OK;
	pApplication->QueryInterface(__uuidof(DTE2), (LPVOID*)&m_pDTE);
	pAddInInst->QueryInterface(__uuidof(AddIn), (LPVOID*)&m_pAddInInstance);
	if(ConnectMode == 5) //5 == ext_cm_UISetup
	{
		HRESULT hr = S_OK;
		CComPtr<IDispatch> pDisp;
		CComQIPtr<Commands> pCommands;
		CComQIPtr<Commands2> pCommands2;
		CComQIPtr<_CommandBars> pCommandBars;
		CComPtr<CommandBarControl> pCommandBarControl;
		CComPtr<Command> pCreatedCommand;
		CComPtr<CommandBar> pMenuBarCommandBar;
		CComPtr<CommandBarControls> pMenuBarControls;
		CComPtr<CommandBarControl> pToolsCommandBarControl;
		CComQIPtr<CommandBar> pToolsCommandBar;
		CComQIPtr<CommandBarPopup> pToolsPopup;

		if (SUCCEEDED(m_pDTE->get_CommandBars(&pDisp))) {
			pCommandBars = pDisp;
			IfFailGoCheck(pCommandBars->get_Item(CComVariant(L"MenuBar"), &pMenuBarCommandBar), pMenuBarCommandBar);
			IfFailGoCheck(pMenuBarCommandBar->get_Controls(&pMenuBarControls), pMenuBarControls);
			IfFailGoCheck(pMenuBarControls->get_Item(CComVariant(L"Tools"), &pToolsCommandBarControl), pToolsCommandBarControl);
			pToolsPopup = pToolsCommandBarControl;
			IfFailGoCheck(pToolsPopup->get_CommandBar(&pToolsCommandBar), pToolsCommandBar);
		}
		else
			return E_FAIL;

		IfFailGoCheck(m_pDTE->get_Commands(&pCommands), pCommands);
		pCommands2 = pCommands;
		if(SUCCEEDED(pCommands2->AddNamedCommand2(m_pAddInInstance, CComBSTR("ToggleFileTabGroup"), CComBSTR("Toggle File (Tab-Groups)"), CComBSTR("Toggle declaration and definition files in alternating tab groups."), VARIANT_TRUE, CComVariant(59), NULL, vsCommandStatusSupported+vsCommandStatusEnabled, vsCommandStylePictAndText, vsCommandControlTypeButton, &pCreatedCommand)) && (pCreatedCommand))
//		if(SUCCEEDED(pCommands->AddNamedCommand(m_pAddInInstance, CComBSTR("ToggleFileTabGroup"), CComBSTR("Toggle File (Tab-Groups)"), CComBSTR("Toggle declaration and definition files in alternating tab groups."), VARIANT_TRUE, 59, NULL, EnvDTE::vsCommandStatusSupported+EnvDTE::vsCommandStatusEnabled, &pCreatedCommand)) && (pCreatedCommand))
		{
			//Add a button to the tools menu bar.
			pDisp = NULL;
			if (pMenuBarCommandBar)
				pCreatedCommand->AddControl(pToolsCommandBar, 1, &pDisp);
		}
		pCreatedCommand = NULL;
		if(SUCCEEDED(pCommands2->AddNamedCommand2(m_pAddInInstance, CComBSTR("ToggleFile"), CComBSTR("Toggle File"), CComBSTR("Toggle declaration and definition files."), VARIANT_TRUE, CComVariant(59), NULL, vsCommandStatusSupported+vsCommandStatusEnabled, vsCommandStylePictAndText, vsCommandControlTypeButton, &pCreatedCommand)) && (pCreatedCommand))
//		if(SUCCEEDED(pCommands->AddNamedCommand(m_pAddInInstance, CComBSTR("ToggleFile"), CComBSTR("Toggle File"), CComBSTR("Toggle declaration and definition files."), VARIANT_TRUE, 59, NULL, EnvDTE::vsCommandStatusSupported+EnvDTE::vsCommandStatusEnabled, &pCreatedCommand)) && (pCreatedCommand))
		{
			//Add a button to the tools menu bar.
			pDisp = NULL;
			if (pMenuBarCommandBar)
				pCreatedCommand->AddControl(pToolsCommandBar, 1, &pDisp);
		}
		return S_OK;
	}
Error:
	return hr;
}

STDMETHODIMP CConnect::OnDisconnection(ext_DisconnectMode /*RemoveMode*/, SAFEARRAY ** /*custom*/ )
{
	m_pDTE = NULL;
	m_pAddInInstance = NULL;
	return S_OK;
}

STDMETHODIMP CConnect::OnAddInsUpdate (SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::OnStartupComplete (SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::OnBeginShutdown (SAFEARRAY ** /*custom*/ )
{
	return S_OK;
}

STDMETHODIMP CConnect::QueryStatus(BSTR bstrCmdName, vsCommandStatusTextWanted NeededText, vsCommandStatus *pStatusOption, VARIANT *pvarCommandText)
{
  if(NeededText == EnvDTE::vsCommandStatusTextWantedNone)
	{
	  if(!_wcsicmp(bstrCmdName, L"ToggleFile.Connect.ToggleFile"))
	  {
		  *pStatusOption = (EnvDTE::vsCommandStatus)(EnvDTE::vsCommandStatusEnabled+EnvDTE::vsCommandStatusSupported);
	  }
	  else if(!_wcsicmp(bstrCmdName, L"ToggleFile.Connect.ToggleFileTabGroup"))
	  {
		  *pStatusOption = (EnvDTE::vsCommandStatus)(EnvDTE::vsCommandStatusEnabled+EnvDTE::vsCommandStatusSupported);
	  }
  }
	return S_OK;
/*  if(NeededText == vsCommandStatusTextWantedNone)
	{
	  if(!_wcsicmp(bstrCmdName, L"ToggleFile.Connect.ToggleFile"))
	  {
		  *pStatusOption = (vsCommandStatus)(vsCommandStatusEnabled+vsCommandStatusSupported);
	  }
  }
	return S_OK; */
}

// CStringT has more member methods, and internal BSTR support.
// typedef CStringT<wchar_t, StrTraitATL< wchar_t, ChTraitsOS< wchar_t > > > wstring;
typedef std::basic_string<WCHAR> wstring;

bool CConnect::TryToOpen(CComPtr<EnvDTE::Documents> & pDocuments, const wstring &filebase, const WCHAR *extension, bool toggleTabGroups)
{
	wstring sDocToOpen(filebase);
	sDocToOpen.append(extension);
	CComBSTR bszDocumentName;
	bszDocumentName = sDocToOpen.c_str();

/*	// Should I try to open it directly, or by way of Documents?
	CComPtr<EnvDTE::Window> pWindow;
	CComBSTR viewKind(EnvDTE::vsViewKindTextView);
	m_pDTE->OpenFile(viewKind, bszDocumentName, &pWindow);
*/

	CComPtr<EnvDTE::Document> pDocument;
	CComBSTR vDocType ( "Auto" );

	CComBSTR docName;
	long numDocuments;
	if (SUCCEEDED(pDocuments->get_Count(&numDocuments))) {
		for(int i = 0; i < numDocuments; ++i) {
			VARIANT vi;
			vi.vt = VT_I4;
			vi.lVal = i+1;
			if (SUCCEEDED(pDocuments->Item(vi, &pDocument))) {
				if (SUCCEEDED(pDocument->get_FullName(&docName))) {
					if (0 == _wcsicmp(sDocToOpen.c_str(), docName.m_str)) {
						// The document may be found, even though its window was closed.
						CComPtr<EnvDTE::Window> pWindow;
						if (SUCCEEDED(pDocument->get_ActiveWindow(&pWindow))) {
							return SUCCEEDED(pDocument->Activate());
						}
						else
							break;
					}
				}
				pDocument = NULL;
			}
		}
	}
	pDocument = NULL;
	bool ret;
	if ((ret = SUCCEEDED(pDocuments->Open(bszDocumentName, vDocType, FALSE, &pDocument))) && toggleTabGroups)
		MoveToOtherTabGroup();
	return ret;

}

void CConnect::MoveToOtherTabGroup()
{
	if (FAILED(m_pDTE->ExecuteCommand(CComBSTR("Window.MovetoNextTabGroup"), CComBSTR("")))) {
		if (FAILED(m_pDTE->ExecuteCommand(CComBSTR("Window.MovetoPreviousTabGroup"), CComBSTR("")))) {
			m_pDTE->ExecuteCommand(CComBSTR("Window.TileVertically"), CComBSTR(""));
		}
	}
}

STDMETHODIMP CConnect::Exec(BSTR bstrCmdName, vsCommandExecOption ExecuteOption, VARIANT * /*pvarVariantIn*/, VARIANT * /*pvarVariantOut*/, VARIANT_BOOL *pvbHandled)
{
	*pvbHandled = VARIANT_FALSE;

	if (ExecuteOption != EnvDTE::vsCommandExecOptionDoDefault)
		return S_OK;

	bool toggleTabGroups(false);

	if (!_wcsicmp(bstrCmdName, L"ToggleFile.Connect.ToggleFileTabGroup"))
		toggleTabGroups = true;
	else if (_wcsicmp(bstrCmdName, L"ToggleFile.Connect.ToggleFile"))
		return S_OK;

	CComPtr<EnvDTE::Document> pActiveDocument;
	*pvbHandled = VARIANT_TRUE;
	if(FAILED(m_pDTE->get_ActiveDocument(&pActiveDocument)))
		return S_OK;
	CComBSTR bszDocumentName;
	if(FAILED(pActiveDocument->get_FullName(&bszDocumentName)))
		return S_OK;
	
	// Make a new string to represent the filename we're constructing
	wstring sDocToOpen(bszDocumentName.m_str);
	int nChars = bszDocumentName.Length();
	wstring::size_type extpos = sDocToOpen.rfind('.');
	if (extpos == wstring::npos) 
		return S_OK;

	if (!_wcsnicmp(&sDocToOpen[extpos], L".c", 2)) {
		sDocToOpen.resize(extpos);

		CComPtr<EnvDTE::Documents> pDocuments;
		if(FAILED(m_pDTE->get_Documents(&pDocuments)))
			return S_OK;

		// try opening the header in the same directory
		if (TryToOpen(pDocuments, sDocToOpen, L".h", toggleTabGroups))
			return S_OK;

		// try changing directories
		wstring::size_type i = sDocToOpen.find(L"\\src\\");
		if (i != wstring::npos) {
			wstring sOtherDoc = sDocToOpen.replace(i, 5, L"\\include\\");
			if (TryToOpen(pDocuments, sOtherDoc, L".h", toggleTabGroups))
				return S_OK;
		}
	}
	else if (!_wcsnicmp(&sDocToOpen[extpos], L".h", 2)) {
		sDocToOpen.resize(extpos);

		CComPtr<EnvDTE::Documents> pDocuments;
		if(FAILED(m_pDTE->get_Documents(&pDocuments)))
			return S_OK;

		if (TryToOpen(pDocuments, sDocToOpen, L".cpp", toggleTabGroups))
			return S_OK;

		// try changing directories
		wstring::size_type i = sDocToOpen.find(L"\\include\\");
		wstring sOtherDirDoc(sDocToOpen);
		if (i != wstring::npos) {
			sOtherDirDoc.replace(i, 9, L"\\src\\");
			if (TryToOpen(pDocuments, sOtherDirDoc, L".cpp", toggleTabGroups))
				return S_OK;
		}

		if (TryToOpen(pDocuments, sDocToOpen, L".c", toggleTabGroups))
			return S_OK;
		if (TryToOpen(pDocuments, sDocToOpen, L".cp", toggleTabGroups))
			return S_OK;
		if (TryToOpen(pDocuments, sDocToOpen, L".idl", toggleTabGroups))
			return S_OK;

		if (i != wstring::npos) {
			if (TryToOpen(pDocuments, sOtherDirDoc, L".c", toggleTabGroups))
				return S_OK;
			if (TryToOpen(pDocuments, sOtherDirDoc, L".cp", toggleTabGroups))
				return S_OK;
		}

	}
	else if (!_wcsnicmp(&sDocToOpen[extpos], L".idl", 4)) {
		sDocToOpen.resize(extpos);

		CComPtr<EnvDTE::Documents> pDocuments;
		if(FAILED(m_pDTE->get_Documents(&pDocuments)))
			return S_OK;

		if (TryToOpen(pDocuments, sDocToOpen, L".h", toggleTabGroups))
			return S_OK;
	}

	return S_OK;
}